package com.devilvan.betrice.lol.controller;

import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.devilvan.betrice.annotation.BetriceRunTime;
import com.devilvan.betrice.common.BetriceResultCode;
import com.devilvan.betrice.common.BetriceResultVo;
import com.devilvan.betrice.exception.BetriceException;
import com.devilvan.betrice.lol.constant.*;
import com.devilvan.betrice.lol.dto.LolBattleChampionDto;
import com.devilvan.betrice.lol.dto.LolBattleHeaderDto;
import com.devilvan.betrice.lol.dto.LolBattleHonourDto;
import com.devilvan.betrice.lol.dto.LolBattleTeamDto;
import com.devilvan.betrice.lol.po.LolBattleChampionPo;
import com.devilvan.betrice.lol.po.LolBattleHeaderPo;
import com.devilvan.betrice.lol.po.LolBattleHonourPo;
import com.devilvan.betrice.lol.po.LolBattleTeamPo;
import com.devilvan.betrice.lol.service.LolBattleChampionService;
import com.devilvan.betrice.lol.service.LolBattleHeaderService;
import com.devilvan.betrice.lol.service.LolBattleHonourService;
import com.devilvan.betrice.lol.service.LolBattleTeamService;
import com.devilvan.betrice.lol.vo.*;
import com.devilvan.betrice.request.BetriceHttpBuilder;
import com.devilvan.betrice.request.component.HttpClientComponent;
import com.devilvan.betrice.request.pojo.BetriceHttpClient;
import com.devilvan.betrice.util.UCollection;
import com.devilvan.betrice.util.UCopy;
import com.devilvan.betrice.util.UEmpty;
import com.devilvan.betrice.util.UString;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.scheduling.annotation.Async;
import org.springframework.transaction.support.TransactionTemplate;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Nonnull;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;

/**
 * @author Evad.Wu
 * @Description LOL-对局英雄信息 控制器
 * @date 2022-09-30
 */
@Slf4j
@RestController
@RequestMapping(value = "lolBattleController")
@Tag(name = "LOL-对局英雄信息 控制器")
@SuppressWarnings(value = "unused")
public class LolBattleController {
    @Resource(name = "httpClientComponent")
    private HttpClientComponent httpClientComponent;
    @Resource(name = "betriceRedisTemplate")
    private RedisTemplate<String, Object> redisTemplate;
    @Resource(name = "transactionTemplate")
    private TransactionTemplate transactionTemplate;

    @Resource(name = "lolBattleHeaderServiceImpl")
    private LolBattleHeaderService lolBattleHeaderService;
    @Resource(name = "lolBattleTeamServiceImpl")
    private LolBattleTeamService lolBattleTeamService;
    @Resource(name = "lolBattleChampionServiceImpl")
    private LolBattleChampionService lolBattleChampionService;
    @Resource(name = "lolBattleHonourServiceImpl")
    private LolBattleHonourService lolBattleHonourService;

    /**
     * 采集LOL对局信息
     *
     * @param request {@link LolBattleListRequestPageVo}
     * @return 形式回调
     */
    @Operation(summary = "采集LOL对局信息")
    @PostMapping(value = "doLolBattle")
    public BetriceResultVo<LolBattleHeaderVo> doLolBattle(@RequestBody String request, @RequestHeader Map<String, String> requestHeader) {
        BetriceResultVo<LolBattleHeaderVo> betriceResultVo = new BetriceResultVo<>();
        String[] subHeaders = new String[]{"host", "origin", "content-type", "referer", "cookie"};
        Map<String, String> headers = UCollection.checkSubMap(requestHeader, subHeaders);
        // 解析报文，附上分页信息offset
        LolBattleListRequestPageVo battleListRequestPageVo = JSONObject.parseObject(request, LolBattleListRequestPageVo.class);
        List<Integer> clPage = this.checkClPage(battleListRequestPageVo.getClPage());
        Integer start = clPage.get(0);
        Integer end = clPage.get(1);
        int count = start;
        while (count <= end) {
            Page<LolBattleListRequestPageVo> page = new Page<>(count, 7);
            // 配置请求List的header
            LolBattleListRequestVo battleListRequestVo = this.listPage2RequestPo(battleListRequestPageVo, page);
            String toLolBattleListPageRequest = JSONObject.toJSONString(battleListRequestVo);
            BetriceResultVo<List<Long>> listBetriceResultVo = this.doLolBattleListCrawler(toLolBattleListPageRequest, headers);
            if (!listBetriceResultVo.isSuccess()) {
                break;
            }
            List<Long> gameIdList = listBetriceResultVo.getData();
            // 解析请求对局列表的报文，赋值给请求Detail的对象使用
            if (Optional.ofNullable(gameIdList).isPresent()) {
                for (Long gameId : gameIdList) {
                    LolBattleDetailRequestVo lolBattleDetailRequestVo = this.lolBattleList2DetailVo(battleListRequestVo, gameId);
                    String detailRequest = JSONObject.toJSONString(lolBattleDetailRequestVo);
                    this.doLolBattleDetailCrawler(detailRequest, headers);
                }
            }
            count++;
        }
        betriceResultVo.result(BetriceResultCode.SUCCESS_200, "操作成功！");
        return betriceResultVo;
    }

    /**
     * 采集LOL对局信息（报文形式）
     *
     * @param request {@link LolBattleHeaderVo}
     * @return 形式回调
     */
    @BetriceRunTime
    @Async(value = "threadPoolExecutor")
    @Operation(summary = "采集LOL对局信息（报文形式）")
    @GetMapping(value = "doLolBattleRequest")
    public CompletableFuture<BetriceResultVo<LolBattleHeaderPo>> doLolBattleRequest(@RequestBody @Nonnull String request) {
        BetriceResultVo<LolBattleHeaderPo> betriceResultVo = new BetriceResultVo<>();
        try {
            JSONObject battleDetail = JSONObject.parseObject(request).getJSONObject("battle_detail");
            // 对局基本信息
            LolBattleHeaderVo lolBattleHeaderVo = battleDetail.toJavaObject(LolBattleHeaderVo.class);
            Long gameId = lolBattleHeaderVo.getGameId();
            // 对局队伍信息
            List<LolBattleTeamVo> teamDetailList = this.parseLolBattleTeamVo(battleDetail, gameId);
            // 对局玩家信息
            List<LolBattleChampionVo> lolBattleChampionVoList = this.parseLolBattleChampionVo(battleDetail, gameId);
            // 对局荣誉信息
            List<LolBattleHonourVo> lolBattleHonourVoList = lolBattleChampionVoList.stream()
                    .map(m -> {
                        String openId = m.getOpenId();
                        LolBattleHonourVo lolBattleHonourVo = m.getLolBattleHonourVo();
                        lolBattleHonourVo.setOpenId(openId);
                        return lolBattleHonourVo;
                    })
                    .peek(p -> p.setGameId(gameId))
                    .toList();
            // 组装
            LolBattleHeaderDto lolBattleHeaderDto = this.copyLolBattleHeaderVo2Dto(lolBattleHeaderVo);
            List<LolBattleTeamDto> lolBattleTeamDtoList = this.copyLolBattleTeamVo2Dto(teamDetailList);
            List<LolBattleChampionDto> lolBattleChampionDtoList = this.copyLolBattleChampionVo2Dto(lolBattleChampionVoList);
            List<LolBattleHonourDto> lolBattleHonourDtoList = this.copyLolBattleHonourVo2Dto(lolBattleHonourVoList);
            // 保存
            transactionTemplate.execute(status -> {
                LolBattleHeaderPo lolBattleHeaderPo = lolBattleHeaderService.saveUpdate(lolBattleHeaderDto);
                List<LolBattleTeamPo> lolBattleTeamPoList = lolBattleTeamService.saveUpdateBatch(lolBattleTeamDtoList);
                List<LolBattleChampionPo> lolBattleChampionPoList = lolBattleChampionService.saveUpdateBatch(lolBattleChampionDtoList);
                List<LolBattleHonourPo> lolBattleHonourPoList = lolBattleHonourService.saveUpdateBatch(lolBattleHonourDtoList);
                betriceResultVo.result(BetriceResultCode.SUCCESS_200, "操作成功！");
                return null;
            });
        } catch (Exception e) {
            e.printStackTrace();
            betriceResultVo.result(BetriceResultCode.FAILED_500, "操作失败：" + e);
        }
        return CompletableFuture.completedFuture(betriceResultVo);
    }

    /**
     * 步骤一：调用wegame获取战绩列表的接口采集数据，并后台保存
     *
     * @param request       请求战绩接口的参数 {@link LolBattleListRequestVo}
     * @param requestHeader 请求头信息
     * @return 形式回调
     */
    @Operation(summary = "步骤一：调用wegame获取战绩列表的接口采集数据，并后台保存")
    @PostMapping(value = "doLolBattleListCrawler")
    public BetriceResultVo<List<Long>> doLolBattleListCrawler(@RequestBody String request, @RequestHeader Map<String, String> requestHeader) {
        BetriceResultVo<List<Long>> betriceResultVo = new BetriceResultVo<>();
        String[] subHeaders = new String[]{"host", "origin", "content-type", "referer", "cookie"};
        Map<String, String> headers = UCollection.checkSubMap(requestHeader, subHeaders);
        List<Long> gameIdList;
        try {
            BetriceHttpClient betriceHttpClient = BetriceHttpBuilder.create()
                    .setHttpRequest(RequestMethod.POST, LolBattleConstant.URL_BATTLE_LIST, request)
                    .setHeaders(headers)
                    .build();
            String body = httpClientComponent.doExecute(betriceHttpClient);
            // 判断用户是否不展示信息
            Integer errorCode = JSONObject.parseObject(body).getJSONObject("result").getInteger("error_code");
            String errorDesc = LolRequestErrorCodeConstant.getDescByCode(errorCode);
            if (Optional.ofNullable(errorDesc).isPresent()) {
                throw new BetriceException(errorDesc);
            }
            // 采集到wegame一页对局的对局ID
            gameIdList = Optional.ofNullable(body)
                    .map(m -> JSONObject.parseObject(m).getJSONArray("battles").toJavaList(LolBattleListVo.class))
                    .orElseThrow(() -> new BetriceException("解析报文失败：" + body))
                    .stream().map(LolBattleListVo::getGameId).toList();
            // 查询对局ID集合中的对局是否已记录，已记录的不处理（取差集）
            gameIdList = lolBattleHeaderService.findDisjunctionByGameIdIn(gameIdList);
            betriceResultVo.result(BetriceResultCode.SUCCESS_200, "操作成功！", gameIdList);
        } catch (BetriceException e) {
            e.printStackTrace();
            betriceResultVo.result(BetriceResultCode.FAILED_500, "操作失败：" + e);
        }
        return betriceResultVo;
    }

    /**
     * 步骤二：接收调用wegame获取战绩明细的接口采集数据，并后台保存
     *
     * @param request       请求战绩接口的参数
     * @param requestHeader 请求头信息
     * @return 形式回调
     */
    @Operation(summary = "步骤二：接收调用wegame获取战绩明细的接口采集数据，并后台保存")
    @PostMapping(value = "doLolBattleDetailCrawler")
    public BetriceResultVo<LolBattleHeaderPo> doLolBattleDetailCrawler(@RequestBody String request, @RequestHeader Map<String, String> requestHeader) {
        String[] subHeaders = new String[]{"host", "origin", "content-type", "referer", "cookie"};
        Map<String, String> headers = UCollection.checkSubMap(requestHeader, subHeaders);
        BetriceHttpClient betriceHttpClient = BetriceHttpBuilder.create()
                .setHttpRequest(RequestMethod.POST, LolBattleConstant.URL_BATTLE_DETAIL, request)
                .setHeaders(headers)
                .build();
        String body = httpClientComponent.doExecute(betriceHttpClient);
        BetriceResultVo<LolBattleHeaderPo> betriceResultVo = null;
        try {
            betriceResultVo = this.doLolBattleRequest(body).get();
        } catch (Exception e) {
            e.printStackTrace();
            redisTemplate.opsForList().rightPush(LolBattleConstant.KEY_BATTLE_DETAIL_REQUEST_EXP, request);
        }
        return betriceResultVo;
    }


    /**
     * 校验查询的起始/终止页数
     *
     * @param clPage 起始页数
     * @return 校验后的起始/终止页数
     */
    public List<Integer> checkClPage(List<Integer> clPage) {
        Integer start = clPage.get(0);
        Integer end = clPage.get(1);
        if (start <= 0) {
            clPage.set(0, 1);
        }
        if (end <= 0) {
            clPage.set(1, 1);
        } else if (end < start) {
            throw new BetriceException(String.format("终止页数不能小于起始页数（始：%d, 终：%d", start, end));
        }
        return clPage;
    }

    /**
     * LolBattleListRequestPageVo -> LolBattleListRequestVo
     *
     * @param battleListRequestPageVo 对局列表信息-入参分页报文
     * @return 对局列表信息-入参报文
     */
    private <T> LolBattleListRequestVo listPage2RequestPo(LolBattleListRequestPageVo battleListRequestPageVo, Page<T> page) {
        LolBattleListRequestVo battleListRequestVo = new LolBattleListRequestVo();
        UCopy.fullCopy(battleListRequestPageVo, battleListRequestVo);
        battleListRequestVo.setOffset(page.offset());
        battleListRequestVo.setCount(page.getSize());
        return battleListRequestVo;
    }

    /**
     * LolBattleListRequestVo -> LolBattleDetailRequestVo
     *
     * @param lolBattleListRequestVo 对局列表请求报文
     * @return 对局明细请求报文
     */
    private LolBattleDetailRequestVo lolBattleList2DetailVo(LolBattleListRequestVo lolBattleListRequestVo, Long gameId) {
        LolBattleDetailRequestVo lolBattleDetailRequestVo = new LolBattleDetailRequestVo();
        lolBattleDetailRequestVo.setAccountType(lolBattleListRequestVo.getAccountType());
        lolBattleDetailRequestVo.setOpenId(lolBattleListRequestVo.getOpenId());
        lolBattleDetailRequestVo.setArea(lolBattleListRequestVo.getArea());
        lolBattleDetailRequestVo.setGameId(gameId);
        lolBattleDetailRequestVo.setFromSrc(lolBattleListRequestVo.getFromSrc());
        return lolBattleDetailRequestVo;
    }

    /**
     * 解析报文中LolBattleChampionVo的部分
     *
     * @param battleDetail 上级结构
     * @param gameId       对局ID
     * @return 解析后的结果集
     */
    private List<LolBattleChampionVo> parseLolBattleChampionVo(JSONObject battleDetail, Long gameId) {
        List<LolBattleChampionVo> lolBattleChampionVoList = Optional.ofNullable(battleDetail.getJSONArray("player_details"))
                .map(m -> m.toJavaList(LolBattleChampionVo.class)).orElseGet(ArrayList::new);
        lolBattleChampionVoList = lolBattleChampionVoList.stream().peek(p -> {
            p.setGameId(gameId);
            p.setName(UString.decode(p.getName()));
        }).toList();
        return lolBattleChampionVoList;
    }

    /**
     * 解析报文中LolBattleTeamVo的部分
     *
     * @param battleDetail 上级结构
     * @param gameId       对局ID
     * @return 解析后的结果集
     */
    private List<LolBattleTeamVo> parseLolBattleTeamVo(JSONObject battleDetail, Long gameId) {
        List<LolBattleTeamVo> teamDetailList = Optional.ofNullable(battleDetail.getJSONArray("team_details"))
                .map(m -> m.toJavaList(LolBattleTeamVo.class)).orElseGet(ArrayList::new);
        teamDetailList = teamDetailList.stream().peek(p -> {
            List<LolBattleTeamBanInfoVo> lolBattleTeamBanInfoVoList =
                    Optional.ofNullable(p.getLolBattleTeamBanInfoVoList()).orElseGet(ArrayList::new);
            for (LolBattleTeamBanInfoVo lolBattleTeamBanInfoVo : lolBattleTeamBanInfoVoList) {
                String pickTurn = lolBattleTeamBanInfoVo.getPickTurn();
                Integer championId = lolBattleTeamBanInfoVo.getChampionId();
                if ("1".equals(pickTurn) || "6".equals(pickTurn)) {
                    p.setBan1(championId);
                } else if ("2".equals(pickTurn) || "7".equals(pickTurn)) {
                    p.setBan2(championId);
                } else if ("3".equals(pickTurn) || "8".equals(pickTurn)) {
                    p.setBan3(championId);
                } else if ("4".equals(pickTurn) || "9".equals(pickTurn)) {
                    p.setBan4(championId);
                } else if ("5".equals(pickTurn) || "10".equals(pickTurn)) {
                    p.setBan5(championId);
                }
            }
            p.setGameId(gameId);
        }).toList();
        return teamDetailList;
    }

    /**
     * 对局荣誉信息 vo转dto
     *
     * @param lolBattleHonourVoList 对局荣誉信息 vo集合
     * @return dto集合
     */
    private List<LolBattleHonourDto> copyLolBattleHonourVo2Dto(List<LolBattleHonourVo> lolBattleHonourVoList) {
        return UCopy.fullCopyCollections(lolBattleHonourVoList, LolBattleHonourDto.class);
    }

    /**
     * 对局英雄信息 vo转dto
     *
     * @param lolBattleChampionVoList 对局英雄信息 vo集合
     * @return dto集合
     */
    private List<LolBattleChampionDto> copyLolBattleChampionVo2Dto(List<LolBattleChampionVo> lolBattleChampionVoList) {
        List<LolBattleChampionDto> lolBattleChampionDtoList = new ArrayList<>(lolBattleChampionVoList.size());
        for (LolBattleChampionVo lolBattleChampionVo : lolBattleChampionVoList) {
            LolBattleChampionDto lolBattleChampionDto = new LolBattleChampionDto();
            UCopy.fullCopy(lolBattleChampionVo, lolBattleChampionDto);
            // position编码转中文
            String position = lolBattleChampionVo.getPosition();
            if (UEmpty.isNotEmpty(position)) {
                String pos = LolBattlePositionConstant.getDescByPosition(position);
                lolBattleChampionDto.setPosition(pos);
            }
            // team编号转中文
            Integer teamId = lolBattleChampionVo.getTeamId();
            String teamDesc = LolBattleTeamConstant.getDescByCode(teamId);
            if (Optional.ofNullable(teamDesc).isPresent()) {
                lolBattleChampionDto.setTeam(teamDesc);
            }
            lolBattleChampionDto.setWin("WIN".equalsIgnoreCase(lolBattleChampionVo.getWin()));
            lolBattleChampionDtoList.add(lolBattleChampionDto);
        }
        return lolBattleChampionDtoList;
    }

    /**
     * 对局队伍信息 vo转dto
     *
     * @param teamDetailList 对局队伍信息 vo集合
     * @return dto集合
     */
    private List<LolBattleTeamDto> copyLolBattleTeamVo2Dto(List<LolBattleTeamVo> teamDetailList) {
        List<LolBattleTeamDto> lolBattleTeamDtoList = new ArrayList<>(teamDetailList.size());
        for (LolBattleTeamVo lolBattleTeamVo : teamDetailList) {
            LolBattleTeamDto lolBattleTeamDto = new LolBattleTeamDto();
            UCopy.fullCopy(lolBattleTeamVo, lolBattleTeamDto);
            Integer teamId = lolBattleTeamVo.getTeamId();
            String teamDesc = LolBattleTeamConstant.getDescByCode(lolBattleTeamVo.getTeamId());
            if (Optional.ofNullable(teamDesc).isPresent()) {
                lolBattleTeamDto.setTeam(teamDesc);
            }
            lolBattleTeamDto.setWin("WIN".equalsIgnoreCase(lolBattleTeamVo.getWin()));
            lolBattleTeamDtoList.add(lolBattleTeamDto);
        }
        return lolBattleTeamDtoList;
    }

    /**
     * 对局基本信息 vo转dto
     *
     * @param lolBattleHeaderVo 对局基本信息Vo
     * @return dto集合
     */
    private LolBattleHeaderDto copyLolBattleHeaderVo2Dto(LolBattleHeaderVo lolBattleHeaderVo) {
        LolBattleHeaderDto lolBattleHeaderDto = new LolBattleHeaderDto();
        UCopy.fullCopy(lolBattleHeaderVo, lolBattleHeaderDto);
        String gameMode = LolBattleGameModeConstant.getDescByMode(lolBattleHeaderVo.getGameQueueId(), lolBattleHeaderVo.getGameMode());
        lolBattleHeaderDto.setGameMode(gameMode);
        return lolBattleHeaderDto;
    }
}
